package cc.unitmesh.devins.idea.renderer.markdown

import cc.unitmesh.markdown.MarkdownTextParser
import org.intellij.markdown.MarkdownElementTypes
import org.intellij.markdown.ast.ASTNode
import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
import org.intellij.markdown.parser.MarkdownParser
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull

/**
 * Tests for MarkdownTextParser utility functions.
 * These tests verify text extraction from Markdown AST nodes.
 */
class MarkdownTextParserTest {

    private val flavour = GFMFlavourDescriptor()
    private val parser = MarkdownParser(flavour)

    // ============ Header Text Extraction Tests ============

    @Test
    fun `should extract ATX header text level 1`() {
        val markdown = "# Hello World"
        val tree = parser.buildMarkdownTreeFromString(markdown)
        val headerNode = findNodeOfType(tree, MarkdownElementTypes.ATX_1)

        assertNotNull(headerNode)
        val text = MarkdownTextParser.extractHeaderText(headerNode, markdown)
        assertEquals("Hello World", text)
    }

    @Test
    fun `should extract ATX header text level 2`() {
        val markdown = "## Section Title"
        val tree = parser.buildMarkdownTreeFromString(markdown)
        val headerNode = findNodeOfType(tree, MarkdownElementTypes.ATX_2)

        assertNotNull(headerNode)
        val text = MarkdownTextParser.extractHeaderText(headerNode, markdown)
        assertEquals("Section Title", text)
    }

    @Test
    fun `should extract ATX header text level 3`() {
        val markdown = "### Subsection"
        val tree = parser.buildMarkdownTreeFromString(markdown)
        val headerNode = findNodeOfType(tree, MarkdownElementTypes.ATX_3)

        assertNotNull(headerNode)
        val text = MarkdownTextParser.extractHeaderText(headerNode, markdown)
        assertEquals("Subsection", text)
    }

    @Test
    fun `should extract SETEXT header text level 1`() {
        val markdown = """
            Main Title
            ==========
        """.trimIndent()
        val tree = parser.buildMarkdownTreeFromString(markdown)
        val headerNode = findNodeOfType(tree, MarkdownElementTypes.SETEXT_1)

        assertNotNull(headerNode)
        val text = MarkdownTextParser.extractHeaderText(headerNode, markdown)
        assertEquals("Main Title", text)
    }

    @Test
    fun `should extract SETEXT header text level 2`() {
        val markdown = """
            Sub Title
            ---------
        """.trimIndent()
        val tree = parser.buildMarkdownTreeFromString(markdown)
        val headerNode = findNodeOfType(tree, MarkdownElementTypes.SETEXT_2)

        assertNotNull(headerNode)
        val text = MarkdownTextParser.extractHeaderText(headerNode, markdown)
        assertEquals("Sub Title", text)
    }

    // ============ Code Fence Content Extraction Tests ============

    @Test
    fun `should extract code fence content without language`() {
        val markdown = """
            ```
            val x = 1
            val y = 2
            ```
        """.trimIndent()
        val tree = parser.buildMarkdownTreeFromString(markdown)
        val codeFenceNode = findNodeOfType(tree, MarkdownElementTypes.CODE_FENCE)

        assertNotNull(codeFenceNode)
        val content = MarkdownTextParser.extractCodeFenceContent(codeFenceNode, markdown)
        assertEquals("val x = 1\nval y = 2", content)
    }

    @Test
    fun `should extract code fence content with language`() {
        val markdown = """
            ```kotlin
            fun hello() = println("Hello")
            ```
        """.trimIndent()
        val tree = parser.buildMarkdownTreeFromString(markdown)
        val codeFenceNode = findNodeOfType(tree, MarkdownElementTypes.CODE_FENCE)

        assertNotNull(codeFenceNode)
        val content = MarkdownTextParser.extractCodeFenceContent(codeFenceNode, markdown)
        assertEquals("fun hello() = println(\"Hello\")", content)
    }

    @Test
    fun `should extract code fence language`() {
        val markdown = """
            ```javascript
            console.log("test");
            ```
        """.trimIndent()
        val tree = parser.buildMarkdownTreeFromString(markdown)
        val codeFenceNode = findNodeOfType(tree, MarkdownElementTypes.CODE_FENCE)

        assertNotNull(codeFenceNode)
        val language = MarkdownTextParser.extractCodeFenceLanguage(codeFenceNode, markdown)
        assertEquals("javascript", language)
    }

    @Test
    fun `should return null for code fence without language`() {
        val markdown = """
            ```
            some code
            ```
        """.trimIndent()
        val tree = parser.buildMarkdownTreeFromString(markdown)
        val codeFenceNode = findNodeOfType(tree, MarkdownElementTypes.CODE_FENCE)

        assertNotNull(codeFenceNode)
        val language = MarkdownTextParser.extractCodeFenceLanguage(codeFenceNode, markdown)
        assertNull(language)
    }

    // ============ Code Span Text Extraction Tests ============

    @Test
    fun `should extract inline code span text`() {
        val markdown = "Use `println()` to print"
        val tree = parser.buildMarkdownTreeFromString(markdown)
        val codeSpanNode = findNodeOfType(tree, MarkdownElementTypes.CODE_SPAN)

        assertNotNull(codeSpanNode)
        val text = MarkdownTextParser.extractCodeSpanText(codeSpanNode, markdown)
        assertEquals("println()", text)
    }

    // ============ Helper Function ============

    private fun findNodeOfType(node: ASTNode, type: org.intellij.markdown.IElementType): ASTNode? {
        if (node.type == type) return node
        for (child in node.children) {
            val found = findNodeOfType(child, type)
            if (found != null) return found
        }
        return null
    }
}

